<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>进程-线程-协程</title>
  
    <meta name="author" content="秀才">

    <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
    <!--[if lt IE 9]>
      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->

    <!-- Le styles -->
    <link href="/assets/twitter/stylesheets/bootstrap.min.css" type="text/css" rel="stylesheet" media="all">
<link href="/assets/twitter/stylesheets/style.css" type="text/css" rel="stylesheet" media="all">
 
    <link href="/assets/twitter/widgets/google_prettify/stylesheets/twitter-bootstrap.css" type="text/css" rel="stylesheet" media="all">

    <!-- Le fav and touch icons -->
  <!-- Update these with your own images
    <link rel="shortcut icon" href="images/favicon.ico">
    <link rel="apple-touch-icon" href="images/apple-touch-icon.png">
    <link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
    <link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
  -->
  </head>

  <body>

    <div class="navbar">
      <div class="navbar-inner">
        <div class="container">
          <a class="brand" href="/">秀才</a>
          <ul class="nav">
            
              


  <li><a href="/archive">存档</a></li>


            
              


  <li><a href="/tags">标签</a></li>


            
              


  <li><a href="/categories">分类</a></li>


            
              


  <li><a href="/pages">页面</a></li>


            
              


  <li><a href="/about">关于我</a></li>


            
          </ul>
        </div>
      </div>
    </div>

    <div class="container">
      <div class="content">
        <div class="page-header">
  <h1>进程-线程-协程 </h1>
</div>

<div class="row">
  <div class="span12">
    <h1>进程-线程-协程</h1>

<h2>简单描述</h2>

<blockquote>
<p>　　进程、线程和协程之间的关系和区别也困扰我一阵子了，最近有一些心得，写一下。
　　进程拥有自己独立的堆和栈，既不共享堆，亦不共享栈，进程由操作系统调度。
　　线程拥有自己独立的栈和共享的堆，共享堆，不共享栈，线程亦由操作系统调度(标准线程是的)。
　　协程和线程一样共享堆，不共享栈，协程由程序员在协程的代码里显示调度。
　　进程和其他两个的区别还是很明显的。
　　协程和线程的区别是：协程避免了无意义的调度，由此可以提高性能，但也因此，程序员必须自己承担调度的责任，同时，协程也失去了标准线程使用多CPU的能力。
　　打个比方吧，假设有一个操作系统，是单核的，系统上没有其他的程序需要运行，有两个线程 A 和 B ，A 和 B 在单独运行时都需要 10 秒来完成自己的任务，而且任务都是运算操作，A B 之间也没有竞争和共享数据的问题。现在 A B 两个线程并行，操作系统会不停的在 A B 　　两个线程之间切换，达到一种伪并行的效果，假设切换的频率是每秒一次，切换的成本是 0.1 秒(主要是栈切换)，总共需要 20 + 19 * 0.1 = 21.9 秒。如果使用协程的方式，可以先运行协程 A ，A 结束的时候让位给协程 B ，只发生一次切换，总时间是 20 + 1 * 0.1 = 20.1 秒。如果系统是双核的，而且线程是标准线程，那么 A B 两个线程就可以真并行，总时间只需要 10 秒，而协程的方案仍然需要 20.1 秒。</p>

<h2>进程</h2>

<p><code>进程的出现是为了更好的利用CPU资源使到并发成为可能</code>。 假设有两个任务A和B，当A遇到IO操作，CPU默默的等待任务A读取完操作再去执行任务B，这样无疑是对CPU资源的极大的浪费。聪明的老大们就在想若在任务A读取数据时，让任务B执行，当任务A读取完数据后，再切换到任务A执行。注意关键字切换，自然是切换，那么这就涉及到了状态的保存，状态的恢复，加上任务A与任务B所需要的系统资源（内存，硬盘，键盘等等）是不一样的。自然而然的就需要有一个东西去记录任务A和任务B分别需要什么资源，怎样去识别任务A和任务B等等。登登登，进程就被发明出来了。通过进程来分配系统资源，标识任务。如何分配CPU去执行进程称之为调度，进程状态的记录，恢复，切换称之为上下文切换。<code>进程是系统资源分配的最小单位</code>，进程占用的资源有：地址空间，全局变量，文件描述符，各种硬件等等资源。</p>
</blockquote>

<h2>线程</h2>

<blockquote>
<p><code>线程的出现是为了降低上下文切换的消耗，提高系统的并发性，并突破一个进程只能干一样事的缺陷，使到进程内并发成为可能</code>。假设，一个文本程序，需要接受键盘输入，将内容显示在屏幕上，还需要保存信息到硬盘中。若只有一个进程，势必造成同一时间只能干一样事的尴尬（当保存时，就不能通过键盘输入内容）。若有多个进程，每个进程负责一个任务，进程A负责接收键盘输入的任务，进程B负责将内容显示在屏幕上的任务，进程C负责保存内容到硬盘中的任务。这里进程A，B，C间的协作涉及到了进程通信问题，而且有共同都需要拥有的东西-------文本内容，不停的切换造成性能上的损失。若有一种机制，可以使任务A，B，C共享资源，这样上下文切换所需要保存和恢复的内容就少了，同时又可以减少通信所带来的性能损耗，那就好了。是的，这种机制就是线程。<code>线程共享进程的大部分资源，并参与CPU的调度, 当然线程自己也是拥有自己的资源的，例如，栈，寄存器等等</code>。 此时，进程同时也是线程的容器。线程也是有着自己的缺陷的，例如健壮性差，若一个线程挂掉了，整一个进程也挂掉了，这意味着其它线程也挂掉了，进程却没有这个问题，一个进程挂掉，另外的进程还是活着。</p>
</blockquote>

<h2>协程</h2>

<blockquote>
<p><code>协程通过在线程中实现调度，避免了陷入内核级别的上下文切换造成的性能损失，进而突破了线程在IO上的性能瓶颈</code>。 当涉及到大规模的并发连接时，例如10K连接。以线程作为处理单元，系统调度的开销还是过大。当连接数很多 —&gt; 需要大量的线程来干活 —&gt; 可能大部分的线程处于ready状态 —&gt; 系统会不断地进行上下文切换。既然性能瓶颈在上下文切换，那解决思路也就有了，在线程中自己实现调度，不陷入内核级别的上下文切换。说明一下，在历史上协程比线程要出现得早，在1963年首次提出, 但没有流行开来。为什么没有流行，没有找到信服的资料，先挖个坑，以后那天了解后，再补上。</p>
</blockquote>

<h2>小结</h2>

<blockquote>
<p>进程，线程，协程不断突破，更高效的处理阻塞，不断地提高CPU的利用率。但是并不是说，线程就一定比进程快，而协程就一定不线程要快。具体还是要看应用场景。可以简单粗暴的把应用分为IO密集型应用以及CPU密集型应用。</p>
</blockquote>

<ul>
<li><p>多核CPU，CPU密集型应用
&gt;此时多线程的效率是最高的，多线程可以使到全部CPU核心满载，又避免了协程间切换造成性能损失。当CPU密集型任务时，CPU一直在利用着，切换反而会造成性能损失，即便协程上下文切换消耗最小，但也还是有消耗的。</p></li>

<li><p>多核CPU，IO密集型应用
&gt;此时采用多线程多协程效率最高，多线程可以使到全部CPU核心满载，而一个线程多协程，则更好的提高了CPU的利用率。</p></li>

<li><p>单核CPU，CPU密集型应用
&gt;单进程效率是最高，此时单个进程已经使到CPU满载了。</p></li>

<li><p>单核CPU，IO密集型应用
&gt;多协程，效率最高。例如，看了上面应该也是知道的了</p></li>
</ul>

<h1>并发与并行</h1>

<h2>并行</h2>

<blockquote>
<p>并发就是指同一时刻有两个或两个以上的“工作单位”在同时执行，从硬件的角度上来看就是同一时刻有两条或两条以上的指令处于执行阶段。所以，多核是并行的前提，单线程永远无法达到并行状态。可以利用多线程和度进程到达并行状态。另外的，Python的多线程由于GIL的存在，对于Python来说无法通过多线程到达并行状态。</p>
</blockquote>

<h2>并发</h2>

<blockquote>
<p>对于并发的理解，要从两方面去理解，</p>
</blockquote>

<ol>
<li>并发设计</li>

<li><p>并发执行。先说并发设计，当说一个程序是并发的，更多的是指这个程序采取了并发设计。</p></li>

<li><p>并发设计的标准：
&gt;<code>使多个操作可以在重叠的时间段内进行</code>，这里的重点在于重叠的时间内， 重叠时间可以理解为一段时间内。例如：在时间1s秒内, 具有IO操作的task1和task2都完成，这就可以说是并发执行。所以呢，单线程也是可以做到并发运行的。当然啦，并行肯定是并发的。一个程序能否并发执行，取决于设计，也取决于部署方式。例如, 当给程序开一个线程（协程是不开的），它不可能是并发的，因为在重叠时间内根本就没有两个task在运行。当一个程序被设计成完成一个任务再去完成下一个任务的时候，即便部署是多线程多协程的也是无法达到并发运行的。</p></li>
</ol>

<h2>并行与并发的关系:</h2>

<blockquote>
<p>并发的设计使到并发执行成为可能，而并行是并发执行的其中一种模式。</p>
</blockquote>

    <hr>
    <div class="pagination">
      <ul>
        <ul>
          
            <li class="prev"><a href="/文摘/%E8%AE%BA%E6%88%90%E7%86%9F%E7%9A%84%E7%94%B7%E9%93%B6/" title="论成熟的男银">&larr; Previous</a></li>
          
          

            <li><a href="/archive">Archive</a></li>

          
          
            <li class="next disabled"><a>Next &rarr;</a>
          
        </ul>
      </ul>
    </div>
    <hr>
    
  </div>
  
  <!-- <div class="span4">
    <h4>Published</h4>
    <div class="date"><span>2016-08-01</span></div>
    <br>
    <h4>Categories</h4>
    <ul class="tag_box">
    
      <li>
  <a href="/categories/#http-ref">http <span>1</span></a>
</li>
    
    </ul>
    <br>
    <h4>Tags</h4>
    <ul class="tag_box">
    
      <li>
  <a href="/tags/#http-ref">http <span>1</span></a>
</li>
    
      <li>
  <a href="/tags/#%E5%B9%B6%E5%8F%91-ref">并发 <span>5</span></a>
</li>
    
    </ul>
  </div> -->
</div>

      </div>

      <footer>
        <p>&copy; 秀才 2013 
          with help from <a href="http://github.com/wendal/gor" target="_blank" title="Gor -- Fast Blog">Gor</a>
          and <a href="http://twitter.github.com/bootstrap/" target="_blank">Twitter Bootstrap</a>
		  and Idea from <a href="http://ruhoh.com" target="_blank" title="The Definitive Technical Blogging Framework">ruhoh</a>
        </p>
      </footer>

    </div> <!-- /container -->
    <script src="//cdnjscn.b0.upaiyun.com/libs/prettify/r298/prettify.min.js"></script>
    <script>
      var pres = document.getElementsByTagName("pre");
      for (var i=0; i < pres.length; ++i) {
        pres[i].className = "prettyprint linenums";
      }
      prettyPrint();
    </script>
    
    
  </body>
</html>
